package strutil

import "unicode"

// splitInfoStrings splits a string into substrings based on upper-case letters.
func splitInfoStrings(strValue string, upperCase bool) []string {
	var runes [][]rune
	lastCharType := 0
	charType := 0
	for _, r := range strValue {
		charType = 0
		if isUpper(r) {
			charType = 1
		} else if isLower(r) {
			charType = 2
		} else if isDigit(r) {
			charType = 3
		} else {
			charType = 4
		}
		if charType == lastCharType {
			runes[len(runes)-1] = append(runes[len(runes)-1], r)
		} else {
			runes = append(runes, []rune{r})
		}
		lastCharType = charType
	}

	for i := 0; i < len(runes)-1; i++ {
		if !isUpper(runes[i][0]) || !isLower(runes[i+1][0]) {
			continue
		}
		length := len(runes[i]) - 1
		temp := runes[i][length]
		runes[i+1] = append([]rune{temp}, runes[i+1]...)
		runes[i] = runes[i][:length]
	}

	var result []string
	for _, rs := range runes {
		if len(rs) <= 0 || (!unicode.IsLetter(rs[0]) && !isDigit(rs[0])) {
			continue
		}
		if upperCase {
			result = append(result, string(toUpperAll(rs)))
			continue
		}
		result = append(result, string(toLowerAll(rs)))
	}

	return result
}

// isDigit reports whether the rune is a digit (category Nd).
func isDigit(r rune) bool {
	return r >= '0' && r <= '9'
}

// isLower reports whether the rune is a lowercase letter (category l).
func isLower(r rune) bool {
	return r >= 'a' && r <= 'z'
}

// isUpper reports whether the rune is an uppercase letter (category L).
func isUpper(r rune) bool {
	return r >= 'A' && r <= 'Z'
}

// toLower converts the given rune to lowercase.
func toLower(r rune) rune {
	if r >= 'A' && r <= 'Z' {
		return r + 32
	}
	return r
}

// toLowerAll converts all runes in the given slice to lowercase.
func toLowerAll(rs []rune) []rune {
	for i := range rs {
		rs[i] = toLower(rs[i])
	}
	return rs
}

// toUpper converts the given rune to uppercase.
func toUpper(r rune) rune {
	if r >= 'a' && r <= 'z' {
		return r - 32
	}
	return r
}

// toUpperAll converts all runes in the given slice to uppercase.
func toUpperAll(rs []rune) []rune {
	for i := range rs {
		rs[i] = toUpper(rs[i])
	}
	return rs
}

// padAtPosition pads the given string to the specified length at the given position.
// It returns the padded string.
func padAtPosition(str string, length int, padStr string, position int) string {
	if len(str) >= length {
		return str
	}

	if padStr == "" {
		padStr = " "
	}

	length = length - len(str)
	startPadLen := 0
	if position == 0 {
		startPadLen = length / 2
	} else if position == 1 {
		startPadLen = length
	}
	endPadLen := length - startPadLen

	charLen := len(padStr)
	leftPad := ""
	cur := 0
	for cur < startPadLen {
		leftPad += string(padStr[cur%charLen])
		cur++
	}

	cur = 0
	rightPad := ""
	for cur < endPadLen {
		rightPad += string(padStr[cur%charLen])
		cur++
	}

	return leftPad + str + rightPad
}

// isLetter reports whether the rune is a letter (category L).
// It's used by the IsContainLetter function.
func isLetter(r rune) bool {
	if !unicode.IsLetter(r) {
		return false
	}

	switch {
	case r >= '\u3034' && r < '\u30ff':
		return false

	// CJK unified ideographs extension A (Chinese, Japanese, and Korean)
	case r >= '\u3400' && r < '\u4dbf':
		return false

	// CJK unified ideographs (Chinese, Japanese, and Korean)
	case r >= '\u4e00' && r < '\u9fff':
		return false

	// CJK compatibility ideographs (Chinese, Japanese, and Korean)
	case r >= '\uf900' && r < '\ufaff':
		return false

	// half-width katakana (Japanese only)
	case r >= '\uff66' && r < '\uff9f':
		return false
	}

	return true
}
